package top.hekun.study.core.visitor.method.simple;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.LocalVariablesSorter;
import top.hekun.study.core.util.MyMethodVisitor5Util;
import top.hekun.study.core.util.MyMethodVisitor6Util;

import static org.objectweb.asm.Opcodes.*;

/**
 * @Author: 贺坤
 * @Date: 2023/5/10 10:16
 */
public class MyMethodVisitor6 extends LocalVariablesSorter {
    private String thisClassName = this.getClass().getSimpleName();
    // 方法描述符
    private Type methodType;
    // 方法开始的时间戳
    private int slotIndex;

    public MyMethodVisitor6(int apiVersion, int access, MethodVisitor methodVisitor, String descriptor) {
        /**
         * api–此访问者实现的ASM api版本。必须是操作代码中的ASMx值之一。
         * access–自适应方法的访问标志。
         * descriptor–方法的描述符（请参见类型）。
         * methodVisitor–此适配器委托调用的方法访问者。
         */
        super(apiVersion, access, descriptor, methodVisitor);
        this.methodType = Type.getMethodType(descriptor);
    }

    @Override
    public void visitCode() {
        System.out.println(thisClassName + ":visitCode");
        // 准备一个 long 类型
        Type longType = Type.LONG_TYPE;
        // 首先，获取一个变量空间 地址
        slotIndex = newLocal(longType);
        // 调用静态方法 获取时间戳
        mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
        // 存储时间戳到 指定位置
        mv.visitVarInsn(longType.getOpcode(ISTORE), slotIndex);
        super.visitCode();

    }


    @Override
    public void visitInsn(int opcode) {

        // 在调用返回前 进行处理 出线异常
        if (opcode == Opcodes.ATHROW) {
            System.out.println(thisClassName + ":visitInsn --- 执行 异常代码前 ");
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitLdcInsn(thisClassName + " 在  test3  异常啦");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

        }
        // 在调用返回前 进行处理 正常返回
        if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
            System.out.println(thisClassName + ":visitInsn --- 执行返回");
            // 准备一个 long 类型
            Type longType = Type.LONG_TYPE;
            //  首先，获取一个变量空间 地址
            int endIndex = newLocal(longType);
            // 调用静态方法 获取时间戳
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
            // 存储时间戳到 指定位置
            mv.visitVarInsn(longType.getOpcode(ISTORE), endIndex);
            // 加载 变量
            mv.visitVarInsn(longType.getOpcode(ILOAD), endIndex);
            mv.visitVarInsn(longType.getOpcode(ILOAD), slotIndex);
            // 执行减法
            mv.visitInsn(longType.getOpcode(ISUB));
            int returnIndex = newLocal(longType);
            // 结果缓存
            mv.visitVarInsn(longType.getOpcode(ISTORE), returnIndex);
            // 加载变量  这步和 上面的 结果缓存 可以一起省略
            mv.visitVarInsn(longType.getOpcode(ILOAD), returnIndex);
            // 加载字符串
            mv.visitLdcInsn("test4");
            // 调用静态打印方法
            mv.visitMethodInsn(INVOKESTATIC, Type.getType(MyMethodVisitor6Util.class).getInternalName(), "printlnRunTime", Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE, Type.getType(String.class)), false);

        }

        super.visitInsn(opcode);
    }

    /**
     *  旧变量映射 方好
     * @param varIndex 旧变量 位置
     * @param type 类型
     * @return 旧变量的新位置
     */
    private int remap_study( int varIndex,  Type type) {
        /**
         * 重新映射的变量数组
         */
        int[] remappedVariableIndices = new int[40];
        // 当 数据位置 + 数据长度 小于 第一个变量的位置, 则表示 获取的是 this 或者 参数
        if (varIndex + type.getSize() <= firstLocal) {
            return varIndex;
        }
        // 存储位置  计算结果为 变量在 remappedVariableIndices 中的角标
        int key = 2 * varIndex + type.getSize() - 1;
        // 数组长度
        int size = remappedVariableIndices.length;
        // 当数组不够存的时候
        if (key >= size) {
            // 构建新数组
            int[] newRemappedVariableIndices = new int[Math.max(2 * size, key + 1)];
            // 复制 数据
            System.arraycopy(remappedVariableIndices, 0, newRemappedVariableIndices, 0, size);
            // 重新定义字段
            remappedVariableIndices = newRemappedVariableIndices;
        }
        // 按照角标获取值
        int value = remappedVariableIndices[key];
        // 当值 等于0 的时候
        if (value == 0) {
            /**
             *   protected int newLocalMapping(final Type type) {
             *     // 返回值 等于 下一个变量的位置
             *     int local = nextLocal;
             *     // 再下一个变量的 位置 等于当前值 + 变量长度
             *     nextLocal += type.getSize();
             *     // 返回位置
             *     return local;
             *   }
             */
            // 获取 参数 的新位置
            value = newLocalMapping(type);
            // 通知子类 旧变量的 新参数位置
            setLocalType(value, type);
            // 在 数组的指定角标位置 存储 变量的(新位置 +1) // 在特殊清空, 静态 无参数方法,的第一个索引为是是0, 会和初始值一样, 所以+1 规避
            remappedVariableIndices[key] = value + 1;
        } else {
            // 如果变量不=0 就-1 ,对应上方的 +1 ,做数据还原
            value--;
        }
        // 返回新变量的值
        return value;
    }

}
